package com.project.weather.component;

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

import com.project.weather.common.PageBean;
import com.project.weather.utils.ReflectUtil;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.executor.statement.RoutingStatementHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.scripting.defaults.DefaultParameterHandler;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.type.TypeHandlerRegistry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.regex.Matcher;

@Intercepts({@Signature(
        method = "prepare",
        type = StatementHandler.class,
        args = {Connection.class, Integer.class}
)})
public class MybatisPageInterceptor implements Interceptor {
    private static final Logger log = LoggerFactory.getLogger(MybatisPageInterceptor.class);
    private String dialect = "mysql";
    private String isShowSql = "no";
    private static long warn_sql_time = 3000L;
    private String pageSqlId = ".*[p|P]ageList.*$";

    public MybatisPageInterceptor() {
    }

    public Object intercept(Invocation invocation) throws Throwable {
        RoutingStatementHandler handler = (RoutingStatementHandler)invocation.getTarget();
        StatementHandler delegate = (StatementHandler) ReflectUtil.getFieldValue(handler, "delegate");
        MappedStatement mappedStatement = (MappedStatement) ReflectUtil.getFieldValue(delegate, "mappedStatement");
        BoundSql boundSql = delegate.getBoundSql();
        if (mappedStatement.getId().matches(this.pageSqlId)) {
            Object obj = boundSql.getParameterObject();
            PageBean<?> page = (PageBean)obj;
            Connection connection = (Connection)invocation.getArgs()[0];
            String sql = boundSql.getSql();
            this.setTotalRecord(page, mappedStatement, connection);
            String pageSql = this.getPageSql(page, sql);
            ReflectUtil.setFieldValue(boundSql, "sql", pageSql);
        }

        long start = System.currentTimeMillis();
        Object returnObject = invocation.proceed();
        long end = System.currentTimeMillis();
        if (log.isInfoEnabled() && "yes".equalsIgnoreCase(this.isShowSql)) {
            String sql = getSql(mappedStatement.getConfiguration(), boundSql, mappedStatement.getId(), end - start);
            log.info(sql);
        }

        return returnObject;
    }

    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    public void setProperties(Properties properties) {
        String property_dialect = properties.getProperty("dialect");
        if (property_dialect != null && !"".equals(property_dialect)) {
            this.dialect = property_dialect;
        } else {
            log.warn("Property dialect is not setted,use default " + this.dialect + "!!!!!!");
        }

        String property_pageSqlId = properties.getProperty("pageSqlId");
        if (property_pageSqlId != null && !"".equals(property_pageSqlId)) {
            this.pageSqlId = property_pageSqlId;
        } else {
            log.warn("Property pageSqlId is not setted,use default " + this.pageSqlId + "!!!!!!");
        }

        String property_isShowSql = properties.getProperty("isShowSql");
        if (this.isShowSql != null && !"".equals(this.isShowSql)) {
            this.isShowSql = property_isShowSql;
        } else {
            log.warn("Property isShowSql is not setted,use default " + this.isShowSql + "!!!!!!");
        }

        String warn_sql_time = properties.getProperty("warn_sql_time");
        if (warn_sql_time != null && !"".equals(warn_sql_time)) {
            MybatisPageInterceptor.warn_sql_time = Long.parseLong(warn_sql_time);
        } else {
            log.warn("Property warn_sql_time is not setted,use default " + warn_sql_time + "!!!!!!");
        }

    }

    private String getPageSql(PageBean<?> page, String sql) {
        StringBuffer sqlBuffer = new StringBuffer(sql);
        if ("mysql".equalsIgnoreCase(this.dialect)) {
            return this.getMysqlPageSql(page, sqlBuffer);
        } else if ("oracle".equalsIgnoreCase(this.dialect)) {
            return this.getOraclePageSql(page, sqlBuffer);
        } else {
            return "postgresql".equalsIgnoreCase(this.dialect) ? this.getPostgresqlPageSql(page, sqlBuffer) : sqlBuffer.toString();
        }
    }

    private String getMysqlPageSql(PageBean<?> page, StringBuffer sqlBuffer) {
        sqlBuffer.append(" limit ").append(page.getLimit()).append(" offset ").append(page.getStart());
        return sqlBuffer.toString();
    }

    private String getPostgresqlPageSql(PageBean<?> page, StringBuffer sqlBuffer) {
        sqlBuffer.append(" limit ").append(page.getLimit()).append(" offset ").append(page.getStart());
        return sqlBuffer.toString();
    }

    private String getOraclePageSql(PageBean<?> page, StringBuffer sqlBuffer) {
        sqlBuffer.insert(0, " select pageInterceptor_table.*,rownum pageInterceptor_rownum from ( ").append(" ) pageInterceptor_table where rownum <= ").append(page.getStart() + (long)page.getLimit());
        sqlBuffer.insert(0, " select * from ( ").append(" )where pageInterceptor_rownum > ").append(page.getStart());
        return sqlBuffer.toString();
    }

    private void setTotalRecord(PageBean<?> page, MappedStatement mappedStatement, Connection connection) {
        BoundSql boundSql = mappedStatement.getBoundSql(page);
        String sql = boundSql.getSql();
        String countSql = this.getCountSql(sql);
        List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
        BoundSql countBoundSql = new BoundSql(mappedStatement.getConfiguration(), countSql, parameterMappings, page);
        ParameterHandler parameterHandler = new DefaultParameterHandler(mappedStatement, page, countBoundSql);
        PreparedStatement pstmt = null;
        ResultSet rs = null;

        try {
            pstmt = connection.prepareStatement(countSql);
            parameterHandler.setParameters(pstmt);
            rs = pstmt.executeQuery();
            if (rs.next()) {
                long recordsTotal = rs.getLong(1);
                page.setRecordsTotal(recordsTotal);
            }
        } catch (SQLException var22) {
            log.error(var22.getMessage(), var22);
        } finally {
            try {
                if (rs != null) {
                    rs.close();
                }

                if (pstmt != null) {
                    pstmt.close();
                }
            } catch (SQLException var21) {
                log.error(var21.getMessage(), var21);
            }

        }

    }

    private String getCountSql(String sql) {
        return "select count(0) from ( " + sql + " ) pageInterceptor_total";
    }

    private static String getSql(Configuration configuration, BoundSql boundSql, String sqlId, long time) {
        String sql = showSql(configuration, boundSql);
        StringBuilder str = new StringBuilder(100);
        str.append(System.getProperty("line.separator"));
        str.append("****** ");
        str.append(sqlId);
        str.append(" : ");
        str.append(sql);
        str.append(System.getProperty("line.separator"));
        str.append("****** ");
        str.append("[" + (new SimpleDateFormat("yyyyMMdd HHmmss")).format(new Date()) + "] ");
        str.append("SQL execution time: " + time + "ms");
        if (time > warn_sql_time) {
            str.append(System.getProperty("line.separator"));
            str.append("!!!!!! this SQL execution time is too long  !!!!!!");
        }

        return str.toString();
    }

    public static String showSql(Configuration configuration, BoundSql boundSql) {
        Object parameterObject = boundSql.getParameterObject();
        List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
        String sql = boundSql.getSql().replaceAll("[\\s]+", " ");
        if (parameterObject != null && parameterMappings.size() > 0) {
            TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
            if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
                sql = sql.replaceFirst("\\?", Matcher.quoteReplacement(getParameterValue(parameterObject)));
            } else {
                MetaObject metaObject = configuration.newMetaObject(parameterObject);
                Iterator var7 = parameterMappings.iterator();

                while(var7.hasNext()) {
                    ParameterMapping parameterMapping = (ParameterMapping)var7.next();
                    String propertyName = parameterMapping.getProperty();
                    Object obj;
                    if (metaObject.hasGetter(propertyName)) {
                        obj = metaObject.getValue(propertyName);
                        sql = sql.replaceFirst("\\?", Matcher.quoteReplacement(getParameterValue(obj)));
                    } else if (boundSql.hasAdditionalParameter(propertyName)) {
                        obj = boundSql.getAdditionalParameter(propertyName);
                        sql = sql.replaceFirst("\\?", Matcher.quoteReplacement(getParameterValue(obj)));
                    } else {
                        sql = sql.replaceFirst("\\?", "缺失");
                    }
                }
            }
        }

        return sql;
    }

    private static String getParameterValue(Object obj) {
        String value = null;
        if (obj instanceof String) {
            value = "'" + obj.toString() + "'";
        } else if (obj instanceof Date) {
            DateFormat formatter = DateFormat.getDateTimeInstance(2, 2, Locale.CHINA);
            value = "'" + formatter.format(new Date()) + "'";
        } else if (obj != null) {
            value = obj.toString();
        } else {
            value = "";
        }

        return value;
    }
}
